#include "Multitree.h"
#include <math.h>
#include <assert.h>

Number * numeric_function(enum FunctionType functtype, Number * num);
Number * numeric_operator(Multitree * tree);

/*this function should get tree without any variables and return number as a result*/
Number * numeric(Multitree * tree){
	Number * num;
	Number * num2;
	Number * to_return;
	if(tree == NULL){
		return NULL;
	}
	to_return = construct_number();
	inttonum(to_return, 0);
	if (tree == NULL){
		inttonum(to_return, 0);
		return to_return;
	}
	switch(tree->symbol.type){
		case NUMBER:
		case CONSTANT:
			return tree->symbol.number;
			break;
		case FUNCTION:
			num = numeric(tree->childs->subtree);
			if(num == NULL){
				return NULL;
			}
			num2 = numeric_function(tree->symbol.func, num);
			if (num2 == NULL){
				return NULL;
			}
			return num2;
			break;
		case OPERATOR:
			return numeric_operator(tree);
			break;
		default:
			assert (tree->symbol.type == VARIABLE);
			/*we cannot return real number here.
			we should throw something*/
			assert(("There should not be variable in a tree when calling numeric.", 0));
			return NULL;
	}
	return to_return; 
}

Number * numeric_operator(Multitree * tree){
	Number * zero = NULL;
	Number * result = NULL;
	Number * tmp1 = NULL;
	Number * tmp2 = NULL;
	ListOfChilds * pass = NULL;
	if (tree == NULL){
		return NULL;
	}
	zero = construct_number();
	result = construct_number();
	inttonum(zero, 0);
	assert(tree->symbol.type == OPERATOR);
	pass = tree->childs;
	if(tree->symbol.oper == POWER){
		tmp1 = numeric(tree->childs->subtree); /*base*/
		tmp2 = numeric(tree->childs->next->subtree); /*exponent*/
		if(tmp1 == NULL || tmp2 == NULL){
			return NULL;
		}
		//we have numbers, now let's just make power of them*/
		numberpow(result, tmp1, tmp2);
	}
	else{
		pass = tree->childs;
		if(tree->symbol.oper == PLUS){
			inttonum(result, 0);
			while (pass != NULL){
				tmp1 = numeric(pass->subtree);
				if(tmp1 == NULL){
					return NULL;
				}
				if (pass->inverted){
					numberminus(result, result, tmp1);
				}else{
					numberplus(result, result, tmp1);
				}
				pass = pass->next;
			}
		}else{
			inttonum(result, 1);
			while (pass != NULL){
				tmp1 = numeric(pass->subtree);
				if (tmp1 == NULL){
					return NULL;
				}
				if (pass->inverted){
					if (numbercmp(tmp1, zero) == 0){
						/* assert(("Division by zero", 0)); */
						return NULL;
					}
					numberdiv(result, result, tmp1);
				}else{
					numbermul(result, result, tmp1);
				}
				pass = pass->next;
			}
		}
	}
	return result;
}

Number * numeric_function(enum FunctionType functtype, Number * num){
	/* This might (should?) be changed so that checks are done in number functions */
	Number * one=NULL;
	Number * zero = NULL;
	Number * result = NULL;
	if(num == NULL){
		return NULL;
	}
	one = construct_number();
	zero = construct_number();
	result = construct_number();

	inttonum(zero, 0);
	inttonum(one, 1);
	switch(functtype){
		case COS:
			if(numbercmp(num, zero)== 0){
				return one;
			}else{
				numbercos(result, num);
				return result;
			}
			break;
		case SIN:
			if(numbercmp(num, zero) == 0){
				return zero;
			}else{
				numbersin(result, num);
				return result;
			}
			break;
		case TAN:
			if(numbercmp(num, zero) == 0){
				return zero;
			}else{
				numbertan(result, num);
				return result;
			}
			break;
		case LN:
			if (numbercmp(num, zero) <0){ /*we do not have logarithms of negative numbers here*/
				return NULL;
			}
			if(numbercmp(num, one) == 0){
				return zero;
			}else{
				numberln(result, num);
				return result;
			}
			break;
	}
	assert(0); /*We should not get here */
	return NULL;
}
